// Printing functions.
#define PrynInternal
#define PrynImplementation
#include <pryn.h>
#include <stdarg.h>

extern PrynResult prynDefaultPrintFunction (PrynState *state, const PrynString *value);

PrynExport (PrynResult, prynPrintString, (PrynState *state, const PrynString *string))
{
	if (!state || !string)
		return prynDefaultPrintFunction (state, string);
		//return PrynResultNullArgument;
	return state->pPrintFunction (state, string);
}

static PrynResult prynPrintIntegerBase (PrynState *state, unsigned int value, int radix)
{
	char buffer [16];
	int offset = sizeof (buffer);
	PrynString string;
	
	do
	{
		buffer [-- offset] = "0123456789abcdef" [value % radix];
		value /= radix;
	}
	while (offset > 0 && value);
	
	prynStringFrom (&string, buffer + offset, sizeof (buffer) - offset);
	return prynPrintString (state, &string);
}

PrynExport (PrynResult, prynPrintInteger, (PrynState *state, int value))
{
//	if (!state) return PrynResultNullArgument;

	if (value < 0)
	{
		prynPrintStringLiteral (state, "-");
		value = -value;
	}

	return prynPrintIntegerBase (state, value, 10);
}

static void prynPrintHex (PrynState *state, unsigned int value)
{
	prynPrintIntegerBase (state, value, 16);
}

PrynExportVariadic (PrynResult, prynPrintFormat, (PrynState *state, const char *format, ...))
{
	va_list argument;
	PrynResult result;

	va_start (argument, format);
	result = prynPrintFormatV (state, format, argument);
	va_end (argument);

	return result;
}

PrynExport (PrynResult, prynPrintFormatV, (PrynState *state, const char *format, va_list argument))
{
	#define print(START, COUNT) prynStringFrom (&string, (START), (int) (COUNT)), prynPrintString (state, &string)
	const char *current = format, *start = format;
	PrynString string;
	
	while (*current)
	{
		if (*current ++ == '%')
		{
			print (start, (current - start) - 1);
			start = current + 1;
			switch (*current ++)
			{
				case 0: print (current - 2, 1); goto end;
				default: print (current - 2, 2); goto end;
				case '%': print (current - 1, 1); break;
					
				case 'r':
					string = va_arg (argument, PrynString);
					prynPrintString (state, &string);
					break;
				
				case 't':
				{
					PrynTime value = va_arg (argument, PrynTime);
					prynTimePrint (&value, state);
					break;
				}
					
				case 'u':
				{
					unsigned int value = va_arg (argument, unsigned int);
					prynPrintIntegerBase (state, value, 10);
					break;
				}
					
				case 'd':
				{
					int value = va_arg (argument, int);
					prynPrintInteger (state, value);
					break;
				}
				
				case 's':
				{
					const char *value = va_arg (argument, const char *);

					prynStringFromZ (&string, value);
					prynPrintString (state, &string);
					break;
				}
				
				case 'x': case 'X':
				{
					unsigned int value = va_arg (argument, unsigned int);
					prynPrintHex (state, value);
					break;
				}
			}
		}
	}
	
	if (current != start)
		print (start, current - start);
	
end:
	return PrynResultSuccess;
	#undef print
}

PrynExport (PrynResult, prynTimePrint, (PrynTime *time, PrynState *state))
{
	if (!time || !state) return PrynResultNullArgument;

	return prynPrintFormat (state, "%d/%s%d/%s%d %s%d:%s%d:%s%d",
		prynTimeYear (time),
		prynTimeMonth (time) < 10 ? "0" : "", prynTimeMonth (time),
		prynTimeDay (time) < 10 ? "0" : "", prynTimeDay (time),
		prynTimeHour (time) < 10 ? "0" : "", prynTimeHour (time),
		prynTimeMinute (time) < 10 ? "0" : "", prynTimeMinute (time),
		prynTimeSecond (time) < 10 ? "0" : "", prynTimeSecond (time));
}
